﻿using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using Devonline.AspNetCore;
using Devonline.Core;
using Devonline.Entity;
using Microsoft.Extensions.Logging;

namespace Devonline.AuxiliaryTools.FileTools;

/// <summary>
/// sql 文件操作相关的服务
/// TODO TBC 尚未实现从 sql 文件的 insert 脚本中读取数据
/// </summary>
public class SqlFileService : DataFileService, IDataFileService, IFileService
{
    /// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="logger">日志</param>
    /// <param name="httpSetting">Http配置项</param>
    public SqlFileService(ILogger<SqlFileService> logger, HttpSetting httpSetting) : base(logger, httpSetting) => _fileExtension = AppSettings.DEFAULT_SQL_FILE_EXTENSION;

    /// <summary>
    /// 写入数据到 sql 文件, 每个对象写一行, 此为大数据量写入处理方法
    /// </summary>
    /// <typeparam name="TEntitySet">写入的数据类型</typeparam>
    /// <param name="entitySets">写入的数据</param>
    /// <param name="fileName">文件名</param>
    /// <param name="converter">对象到数据行的转换方法, 传入参数分别是: 当前行字符串, 当前行号, 总行号</param>
    /// <returns>写入的总行数</returns>
    public override async Task<long> WriteAsync<TEntitySet>(IEnumerable<TEntitySet> entitySets, string? fileName = null, Func<TEntitySet, long, long, string?>? converter = default)
    {
        var index = 0L;
        if (!entitySets.Any())
        {
            return index;
        }

        var (propertyInfos, insertColumns) = BeforeWrite<TEntitySet>();
        var total = entitySets.LongCount();
        var type = typeof(TEntitySet);
        var typeName = type.GetDisplayName();
        var tableName = type.GetTableName();
        fileName ??= tableName + _fileExtension;
        fileName = GetFileName<TEntitySet>(fileName);
        _logger.LogInformation($"将写入 {typeName} 数据 {total} 行到文件: {fileName} 中!");

        var line = string.Empty;
        using var writer = GetWriter(fileName);

        try
        {
            await writer.WriteLineAsync($"-- {tableName} start --");
            foreach (var entitySet in entitySets)
            {
                index++;
                line = converter is null ? ConvertTo(entitySet, index, total, propertyInfos) : converter(entitySet, index, total);
                if (!string.IsNullOrWhiteSpace(line))
                {
                    if ((index - 1) % _httpSetting.Execute.MaxWriteCount == 0)
                    {
                        await writer.WriteLineAsync(insertColumns);
                    }

                    await writer.WriteLineAsync(line);
                }
            }

            await writer.WriteLineAsync($"-- {tableName} end --");
            _logger.LogInformation($"已写入 {typeName} 数据 {index} 行到文件: {fileName} 中!");
        }
        catch (Exception ex)
        {
            throw new Exception($"写入 {typeName} 数据第 {index} 行数据到文件: {fileName} 发生异常, 错误对象: {line}", ex);
        }
        finally
        {
            await writer.FlushAsync();
            writer.Close();
        }

        return index;
    }

    /// <summary>
    /// 获取可隐射到数据库的字段列表
    /// </summary>
    /// <typeparam name="TEntitySet"></typeparam>
    /// <returns></returns>
    private static PropertyInfo[] GetPropertyInfos<TEntitySet>() => typeof(TEntitySet).GetProperties().Where(x => x.HasAttribute<ColumnAttribute>() && !x.HasAttribute<NotMappedAttribute>()).ToArray();
    /// <summary>
    /// 写入之前计算全局属性
    /// </summary>
    /// <typeparam name="TEntitySet"></typeparam>
    private (PropertyInfo[], string) BeforeWrite<TEntitySet>()
    {
        var propertyInfos = GetPropertyInfos<TEntitySet>();
        var quotes = _httpSetting.DatabaseType.GetDatabaseSeparator();
        var left = char.MinValue;
        var right = char.MinValue;
        if (quotes.Length >= 1)
        {
            left = quotes[0];
            right = quotes.Length >= 2 ? quotes[1] : quotes[0];
        }

        var insertColumns = $"INSERT INTO {left}{typeof(TEntitySet).GetTableName()}{right} ({string.Join(AppSettings.DEFAULT_SPLITER_STRING, propertyInfos.Select(x => left + x.GetColumnName() + right))}) VALUES ";
        return (propertyInfos, insertColumns);
    }
    /// <summary>
    /// 将对象转换为字符串表达形式
    /// </summary>
    /// <typeparam name="TEntitySet"></typeparam>
    /// <param name="entitySet"></param>
    /// <param name="index"></param>
    /// <param name="total"></param>
    /// <param name="propertyInfos"></param>
    /// <returns></returns>
    private string ConvertTo<TEntitySet>(TEntitySet entitySet, long index, long total, PropertyInfo[] propertyInfos)
    {
        if (entitySet is IEntitySet entity)
        {
            var sqls = new List<string>();
            foreach (var propertyInfo in propertyInfos!)
            {
                sqls.Add(entity.GetSqlStringValue(propertyInfo) ?? "NULL");
            }

            var sql = $"({string.Join(AppSettings.DEFAULT_SPLITER_STRING, sqls)})";
            if (index == total || index % _httpSetting.Execute.MaxWriteCount == 0)
            {
                sql += AppSettings.CHAR_SEMICOLON;
            }
            else
            {
                sql += AppSettings.CHAR_COMMA;
            }

            return sql;
        }

        return string.Empty;
    }
}